/*********************************************************************************
Organization 					: 				Georgia Institute of Technology
  
Institute 						:				Cognitive Computing Group(CCL)
 
Authors							: 				Kane
 												
Class							:				Proxy Bot
 
Function						: 				The class provides to send commands
												to wargus over a socket connectio
****************************************************************************/
package base;
import java.util.*;

/**
 * Reimplementation of GameProxy.cpp in Java
 * renamed as ProxyBot.java for compatibility reasons
 * @author Sooraj Bhat
 * @author Kane Bonnette
 */

// This class allows you to interface with Stratagus::Wargus
// Written by Benjamin Brewster
// Send questions, suggestions, complaints to ben@brewsters.net
import java.awt.Point;
import java.awt.geom.Point2D;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.StringTokenizer;

import units.AntUnit;



/**
 * Handles all communication between ABL and Wargus. Caches Wargus info
 * for ABL requests
 * @author kane
 */
public class ProxyBot implements Runnable {

	/**
	 * Holds the map: row 1, col 4 is map[1][4] - originally holds tildes 
	 * (~) at initialization
	 */
	private char[][] map;

	private String savedFilePath = " ";
	/**
	 * Holds the map flags 
	 */
	private int [][]mapflags;
	/**
	 * Holds the visible flags
	 */
	private char[][] mapVisible;
	
	/**
	 * Holds the map tiles 
	 */
	private int [][]maptiles;
	
	private WargusLuaState luastate;
	
	/**
	 * Holds all of the units under this agent's control - the index into this 
	 * is the actual global unitID for this unit
	 */
	private ArrayList<WargusUnit> myUnits;
	private ArrayList<WargusUnit> tempUnits;

	/**
	 * All units known to be enemies - the index into this is the actual 
	 * global unitID for this unit
	 */
	private ArrayList<WargusUnit> enemyUnits;

	/**
	 * All other units - friends, neutrals, or unknowns - the index into this 
	 * is the actual global unitID for this unit
	 */
	private ArrayList<WargusUnit> friendOrNeutralUnits;

	private boolean m_paused = false;
	private String m_serverAddress = null;
	private int[] playerID; // The player IDs, Our player is first
	/** true if human, false if orc */
	private boolean[] race;
	private int mapWidth = 0;
	private int mapLength = 0;
	private long gold = 0;
	private long wood = 0;
	private long oil = 0;
	private long food = 0;
	private long demand = 0;
	private long currCycle = 0;
	private Socket mySocket = null; // This is how the proxy communicates with Stratagus
	private InputStream mySocketIn= null;
	private OutputStream mySocketOut= null;
	/** Make sure to create a new bot if we have more than one player
	 * on the same machine*/
	private static ProxyBot proxy;
	private int numOfPlayers;
	/** locks the send/receive method to prevent deadlock*/
	private boolean sendLock = false;
	/** Locks the state modification to prevent concurrent modification */ 
	private boolean stateLock = false;
	/** holds whether each advance (improve shields, Ranger Upgrade etc) */
	private boolean[] advances;
	/** whether to run with debugging on */
	private boolean debug;
	private String last_message_sent = ""; 
	private boolean m_connected = false;
	private boolean m_reseted = false;	// Stores whether the ProxyBat has at least got the state o the game once
	private boolean m_stopSignal = false;

	
	/** Stores the list of messages that have been ignored by Stratagus, to resend them again */
	private LinkedList<String> ignoredMessages = new LinkedList<String>();	
	
	//DEBUG
	static PrintStream out;
	
	public void stop() {
		m_stopSignal=true;
	}
	
	public boolean stopped() {
		return m_stopSignal;
	}	
	
	public synchronized boolean lockStateCheck() {
		return stateLock;
	}
	
	public synchronized void lockState() throws InterruptedException {
		while(stateLock) {
			System.out.flush();
			wait(); 
		}
		stateLock = true;
	}

	public synchronized void unlockState() {
		stateLock = false;
		notifyAll();
	}
	
	public synchronized void lockSend() throws InterruptedException {
		while(sendLock) {
			System.out.flush();
			wait(); 
		}
		sendLock = true;
	}

	public synchronized void unlockSend() {
		sendLock = false;
		notifyAll();
	}
	
	public long getGold() {
		return gold;
	}
	
	public long getWood() {
		return wood;
	}
	
	public long getOil() {
		return oil;
	}
	
	public long getDemand() {
		return demand;
	}

	public long getFood() {
		return food;
	}
	
	void connect() {
		try {
			proxy = this;
			luastate = new WargusLuaState();
			mySocket = new Socket();
			mySocket.setTcpNoDelay(true);
			mySocket.setSoLinger(true, 100);
			mySocket.connect(new InetSocketAddress(m_serverAddress, 4870));
			mySocketIn = new BufferedInputStream(mySocket.getInputStream());
			mySocketOut = new BufferedOutputStream(mySocket.getOutputStream());
			out = new PrintStream(new FileOutputStream("ProxyBot_SentMessages.txt"));
			m_connected = true;
		} catch (Exception e) {
			System.out.println("ProxyBot: Unable to connect to stratagus!");
			m_connected = false;
		}		
	}
	
	/**
	 * handles socket creation and calls next constructor
	 * @param serverAddress - address in either hostname or IP
	 */
	public void reset()
	{		
		if (m_connected) {
			String[] response;	
			response = sendAndReceiveMessage("LISPGET GAMEINFO\n", false).split(" ");
			//#s(gameinfo player-id 0 race 0 width 32 length 32 numPlayers 2 )
			currCycle = 0;
			numOfPlayers = Integer.parseInt(response[10]);
			playerID = new int[numOfPlayers];
			race = new boolean[numOfPlayers];
			playerID[0] = Integer.parseInt(response[2]);
			race[0] = (response[4].contains("0") ? true : false);
			mapWidth = Integer.parseInt(response[6]);
			mapLength = Integer.parseInt(response[8]);
			//Initialize the map
			map = new char[mapLength][mapWidth];
			for (int rowIndex = 0; rowIndex < mapLength; rowIndex++)
				for (int columnIndex = 0; columnIndex < mapWidth; columnIndex++)
					map[rowIndex][columnIndex] = '~';
			gold = wood = oil = 0;
			myUnits = new ArrayList<WargusUnit>();
			enemyUnits = new ArrayList<WargusUnit>();
			friendOrNeutralUnits = new ArrayList<WargusUnit>();
			advances = new boolean[53];
			for(int i = 0; i < 53; i++) {
				advances[i] = false;
			}
			GetMapState();
			try {
				GetState();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		//	getResearch();
			m_reseted = true;
		}
	}
	
	
	public ProxyBot(String serverAddress, boolean bugging) {
		
		tempUnits = new ArrayList<WargusUnit>();
		m_serverAddress = serverAddress;
		debug = bugging;	
		connect();
		reset();
	}

	/**
	 * threaded means of collecting info from Stratagus
	 */
	public void run() {
		if (!debug && m_connected) {
			sendAndReceiveMessage("SPEED -1", true);
		}
		while(!m_stopSignal) {
			try {
				if (connected()) {
					if (!m_paused) {
						//System.out.println("PROXYBOT: cycle " + currCycle + "(" + ignoredMessages.size() + " ignored)");
						GetMapState();
						GetState();
				//		GetMapFlags();
				//		GetMapTiles();
				//		getResearch();
						// since we aren't getting the data every cycle, move forward 10
						// increase to decrease granularity and increase speed
						// lower to increase granularity and decrease speed
						AdvanceNCycles(10);
						currCycle += 10;
						if (ignoredMessages.size()>0) {
							LinkedList<String> tmp = new LinkedList<String>();
							String msg;
							System.out.println("PROXYBOT: " + ignoredMessages.size() + " ignored messages");
		
							while(ignoredMessages.size()>0) {
								tmp.add(ignoredMessages.removeFirst());				
							}
						
							while(tmp.size()>0) {
								msg = tmp.removeFirst();
								System.out.println("PROXYBOT: Resending Message: " + msg);
								sendAndReceiveMessage(msg,true);				
							}
						}
					}
				} else {
					connect();
					reset();
				}

				Thread.sleep(10);
			} catch (Exception e) {
				System.out.print(e.getStackTrace());
			}
		}
	}
	
	/**
	 * Get the Lua State
	 */
	public void GetLuaState()
	{
		String response = " ";
		String arrayname = " ";
		String funcname = " ";
		String index = " ";
		String delims = "() ";
		//getting the map
		try{
			response = sendAndReceiveMessage("LISPGET LUA\n", false);
			lockState();			
			StringTokenizer tokenizer = new StringTokenizer(response, delims);			
			arrayname = tokenizer.nextToken(); 
			funcname = tokenizer.nextToken(); 
			index = tokenizer.nextToken(); 
			luastate.setState(arrayname, funcname, index);
			unlockState();			
		} catch (Exception e) {
			unlockState();
			System.out.println("ProxyBot: Exception while getting the Lua State, retrying!");
			GetLuaState();//try again, we didn't screw our map last time
		}
		
	}
	
	public void LoadSavedFileName(String filename) {
		String response = "";
		try{
			response = sendAndReceiveMessage("FORWARDSTATE " + filename + "\n", false);
			if (!response.startsWith("OK")) throw new Exception();
		} catch (Exception e) {
			System.out.println("ProxyBot: Exception while loading the saved Map State, retrying!");
			LoadSavedFileName(filename);
		}
	}
	
	public void GetSavedFileName()
	{
		String response = " ";
		try{
			response = sendAndReceiveMessage("LISPGET ZSAVEFILENAME\n", false);
			lockState();	
			savedFilePath = response;
			unlockState();			
		} catch (Exception e) {
			unlockState();
			System.out.println("ProxyBot: Exception while getting the saved Map State, retrying!");
			GetSavedFileName();//try again, we didn't screw our map last time
		}		
	}
	
	public String GetSaveMapFileName()
	{
		return savedFilePath;		
	}
	
	public void GetMapFlags() {
		int rowIndex = 0;
		int columnIndex = 0;
		String response = "";
		String mapRow;
		String delims = "()\n";
		String rowdelims = " ";
		
		int[][] myMap = new int[mapWidth][mapLength];
		//getting the map
		try{
			response = sendAndReceiveMessage("LISPGET FLAGS\n", false);
//			System.out.println(response);
			//tokenize the map response; each tile is a char
			lockState();			
			StringTokenizer tokenizer = new StringTokenizer(response, delims);			
			mapRow = tokenizer.nextToken(); // first row of map
			while (mapRow != null) {
				String []rowelements = mapRow.split("\\s");
				for (columnIndex = 0; columnIndex < mapWidth; columnIndex++)
					// Save all columns in this row to the map variable
				{
					String element = rowelements[columnIndex];
					myMap[rowIndex][columnIndex] = Integer.parseInt(element);
				}
				if (tokenizer.hasMoreTokens()) {
					mapRow = tokenizer.nextToken();
					rowIndex++; 
				} else {
					mapRow = null;
				}
			}
			mapflags = myMap;
			unlockState();			
		} catch (Exception e) {
			unlockState();
			System.out.println("ProxyBot: Exception while getting the map flags, retrying!");
			System.out.println("Received: " + response);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			GetMapFlags();//try again, we didn't screw our map last time
		}
	} //End void GetMapState()

	public void GetMapTiles() {
		int rowIndex = 0;
		int columnIndex = 0;
		String response = "";
		String mapRow;
		String delims = "()\n";
		String rowdelims = " ";
		
		int[][] myMap = new int[mapWidth][mapLength];
		//getting the map
		try{
			response = sendAndReceiveMessage("LISPGET TILES\n", false);
//			System.out.println(response);
			//tokenize the map response; each tile is a char
			lockState();			
			StringTokenizer tokenizer = new StringTokenizer(response, delims);			
			mapRow = tokenizer.nextToken(); // first row of map
			while (mapRow != null) {
				String []rowelements = mapRow.split("\\s");
				for (columnIndex = 0; columnIndex < mapWidth; columnIndex++)
					// Save all columns in this row to the map variable
				{
					String element = rowelements[columnIndex];
					myMap[rowIndex][columnIndex] = Integer.parseInt(element);
				}
				if (tokenizer.hasMoreTokens()) {
					mapRow = tokenizer.nextToken();
					rowIndex++; 
				} else {
					mapRow = null;
				}
			}
			maptiles = myMap;
			unlockState();			
		} catch (Exception e) {
			unlockState();
			System.out.println("ProxyBot: Exception while getting the map tiles, retrying!");
			GetMapTiles();//try again, we didn't screw our map last time
		}
	} //End void GetMapState()

	
	public String GetMapTile(int row, int col) {
		return Integer.toString(maptiles[row][col], 10);
	}
	
	public String GetMapFlag(int row, int col) {
		return Integer.toString(mapflags[row][col], 10);
	}
	/**
	 * Get the Lua State 
	 */
	public WargusLuaState getLua()
	{
		return luastate;
	}
	
	
	/**
	 * Sends LISPGET RESEARCH, and parses results for advances acquired
	 */
	public void getResearch() {
		String[] response = sendAndReceiveMessage("LISPGET RESEARCH\n", false).split(" ");
		if (!response[0].contains("research")) {
			return;
		}
				
		for(int i = 2; i < response.length; i+=2) {
			if(response[i].contains("R")) {				
				try {
					int j = WargusStateImporter.researchStringToType(response[i-1]);
					advances[j]=true;
				} catch (Exception e) {
//					System.out.println("Proxybot.getResearch: unknown research: " + response[i-1]);					
				}				
			} else {
				try {
					int j = WargusStateImporter.researchStringToType(response[i-1]);
					advances[j]=false;
				} catch (Exception e) {
//					System.out.println("Proxybot.getResearch: unknown research: " + response[i-1]);					
				}				
			}
		}
	}

	/**
	 * Sends LISPGET STATE, and parses results for player info and unit
	 * information. Stores info in appropriate fields
	 * @throws InterruptedException 
	 */
	public synchronized void GetState() throws InterruptedException {
		String response;
		String globalState[];
		String globalStateAsAWord = "#S(global-state"; 
		int globalStatePosition = 0; 
		String unitAsAWord = "#s(unit"; 
		String paren = "(";
		String spaceAndParens = " ()";
		int unitStartIndex = 0;
		int unitEndIndex = 0; 
		String unitString; 
		boolean haltUnitSearch = false; 
		int unitID; 
		int playerIDOfUnit;
		int type;
		int locX;
		int locY;
		int hp;
		int mp;
		int resourceAmount;
		int kills;
		int lastAttacked;
		int[] statusArgs = new int[3];
		
		response = sendAndReceiveMessage("LISPGET STATE\n", false);

		lockState();	
		
		tempUnits.clear();
		for(int i=0; i< myUnits.size(); i++)
			tempUnits.add(myUnits.get(i));
		
		myUnits.clear();
		
		friendOrNeutralUnits.clear();
		enemyUnits.clear();

		// Find where the global state is given
		globalStatePosition = response.indexOf(globalStateAsAWord);
		int endOfGlobalState = response.indexOf(")", globalStatePosition);
		if (globalStatePosition != -1){ // Make sure the global state was found
			globalState =  response.substring(globalStatePosition,
					endOfGlobalState).split(" "); 
			//#S(global-state :gold %d :wood %d :oil %d :food %d :demand %d )
			gold = Integer.parseInt(globalState[2]);
			wood = Integer.parseInt(globalState[4]);
			oil = Integer.parseInt(globalState[6]);
			food = Integer.parseInt(globalState[8]);
			demand = Integer.parseInt(globalState[10]);
		}

		/**
		 * Record all unit states
		 */
		while (true) {
			statusArgs = new int[3]; //need so the same array isn't passed to all Units
			// Look for the start of a unit, starting at the end of the 
			//previous unit - unitEndIndex starts at zero the first time
			unitStartIndex = response.indexOf(unitAsAWord, unitEndIndex);
			unitEndIndex = response.indexOf(unitAsAWord, unitStartIndex + 1);

			if (unitEndIndex == -1) {
				// We found a unit, but there is no next unit - 
				//this the normal loop termination method
				haltUnitSearch = true; 
				unitStartIndex = response.lastIndexOf(paren, unitStartIndex - 1);
				unitEndIndex = response.length();
			} else {
				// We found a unit, and are aware of the next unit
				unitStartIndex = response.lastIndexOf(paren, unitStartIndex - 1);
				unitEndIndex = response.lastIndexOf(paren, unitEndIndex - 1);
			}
			//System.out.println("Start: " + unitStartIndex + " End: " + unitEndIndex);
			if(unitEndIndex == -1 || unitStartIndex == -1) {
//				System.out.println("GetState: unlock");
				unlockState();
				return;//something is fubar
			}
			unitString =  response.substring(unitStartIndex, 
					unitEndIndex);
			//( 1 . #s(unit player-id 0 type 0 loc (1 4) hp 60 r-amt 0 kills 0 status 1 status-args ()))
			StringTokenizer unitTokenizer = new StringTokenizer(unitString,
					spaceAndParens);
					
			unitID = Integer.parseInt(unitTokenizer.nextToken());
			unitTokenizer.nextToken(); // Eat the String "."
			unitTokenizer.nextToken(); // Eat the String "#s"
			unitTokenizer.nextToken(); // Eat the String "unit"
			unitTokenizer.nextToken(); // Eat the String "player-id"
			playerIDOfUnit = Integer.parseInt(unitTokenizer.nextToken());
			unitTokenizer.nextToken(); // Eat the String "type"
			type = Integer.parseInt(unitTokenizer.nextToken()); 
			unitTokenizer.nextToken(); // Eat the String "loc"
			locX = Integer.parseInt(unitTokenizer.nextToken()); 
			locY = Integer.parseInt(unitTokenizer.nextToken()); 
			unitTokenizer.nextToken(); // Eat the String "hp"
			hp = Integer.parseInt(unitTokenizer.nextToken());
			unitTokenizer.nextToken(); // Eat the String "mp"
			mp = Integer.parseInt(unitTokenizer.nextToken());
			unitTokenizer.nextToken(); // Eat the String "r-amt"
			resourceAmount = Integer.parseInt(unitTokenizer.nextToken()); 
			unitTokenizer.nextToken(); // Eat the String "kills"
			kills = Integer.parseInt(unitTokenizer.nextToken());
			unitTokenizer.nextToken().trim(); // Eat the String "last-attacked"
			lastAttacked = Integer.parseInt(unitTokenizer.nextToken());		
			unitTokenizer.nextToken(); // Eat the String "status"
			statusArgs[0] = Integer.parseInt(unitTokenizer.nextToken());
			unitTokenizer.nextToken().trim(); // Eat the String "status-args"

			for(int i = 1; i < 3;i++) {
				if (!unitTokenizer.hasMoreTokens()) break;
				try {
					statusArgs[i] = Integer.parseInt(unitTokenizer.nextToken());
				} catch (NumberFormatException e) {
					//No more status args, last unit, and trying to parse the global state (whoops)
				}
			}

			
			if (playerIDOfUnit == playerID[0]){ // Its our unit
				WargusUnit newUnit = new WargusUnit(unitID, type,
											  		hp, mp, locX, locY, resourceAmount,
											  		kills, lastAttacked, statusArgs, playerIDOfUnit); 
				
				boolean found = false;
				for(int j=0; j<tempUnits.size(); j++)
				{
					
					
					WargusUnit temp_unit = tempUnits.get(j);

					if(temp_unit.getUnitID() == newUnit.getUnitID())
					{
						 found = true;
						
						if(temp_unit.getLocY() == newUnit.getLocY() && temp_unit.getLocX() ==  newUnit.getLocX())
						{
							newUnit.setHeading(temp_unit.getHeading());
						}
						
						else
						{
							newUnit.setHeading(Math.atan2( -temp_unit.getLocY() + newUnit.getLocY() , -temp_unit.getLocX() +  newUnit.getLocX()));
						}
					
						//newUnit.setRandom(temp_unit.getRandom());
						
						//System.out.println("ProxyBot: temp_unit("+temp_unit.getUnitID()+"): " + temp_unit.getUnitStatus());
						if (temp_unit.getUnitStatus() == null) {
							System.out.println("Found: here");
							newUnit.setUnitStatusInitial("idle");
						} else {
							newUnit.setUnitStatusInitial(temp_unit.getUnitStatus());
						}
						
						//if(newUnit.getStatus()[0] == 0 && temp_unit.getStatus()[0] != 0)
						//newUnit.setUnitStatus("idle");

						break;
					}
				}
				
				if(tempUnits.size() == 0 || !found)
				{
					String printMe = "";
					if (tempUnits.size() == 0){
						printMe += "tempUnits.size == 0";
					}
					if (!found) {
						printMe += " found == false";
					}
					
				
					//System.out.println("Not found(" + newUnit.getUnitID() + "): Here -- " + printMe);
					newUnit.setHeading(-100);
					newUnit.setUnitStatusInitial("idle");
				}
				
				myUnits.add(newUnit);
			} else if (playerIDOfUnit == 15) { // It's a friendly or neutral unit
				WargusUnit newUnit = new WargusUnit(unitID, type,
													hp, mp,locX, locY, resourceAmount,
													kills, lastAttacked, statusArgs, playerIDOfUnit); 
				
				friendOrNeutralUnits.add(newUnit);
			} else { // It's an enemy unit!
				WargusUnit newUnit = new WargusUnit(unitID, type,
													hp, mp, locX, locY, resourceAmount,
													kills, lastAttacked, statusArgs, playerIDOfUnit); 
				
				enemyUnits.add(newUnit);
			}

			if (haltUnitSearch == true) 
				break;
		} // End while (true)
		unlockState();
	} // End void GetUnitStates(ClientSocket& inc_socketAccessToGame)

	/**
	 * Gets the state of the map and puts it into map.
	 * We initialize mapWidth/Length in constructor, stopped resetting it every
	 * time this was run. 
	 */
	public void GetMapState() {
		int rowIndex = 0;
		int columnIndex = 0;
		String response = "";
		String mapRow;
		String delims = "()\n";
		char[][] myMap = new char[mapWidth][mapLength];
		//getting the map
		try{
			response = sendAndReceiveMessage("LISPGET MAP\n", false);
//			System.out.println(response);
			//tokenize the map response; each tile is a char
			lockState();			
			StringTokenizer tokenizer = new StringTokenizer(response, delims);			
			mapRow = tokenizer.nextToken(); // first row of map
			while (mapRow != null) {
//				System.out.println(mapRow + " " + "Width: " + mapWidth);
				for (columnIndex = 0; columnIndex < mapWidth; columnIndex++)
					// Save all columns in this row to the map variable
					myMap[rowIndex][columnIndex] = mapRow.charAt(columnIndex);
				if (tokenizer.hasMoreTokens()) {
					mapRow = tokenizer.nextToken();
					rowIndex++; 
				} else {
					mapRow = null;
				}
			}
			map = myMap;
			unlockState();			
		} catch (Exception e) {
			unlockState();
			//System.out.println("ProxyBot: Exception while getting the map, retrying!");
//			System.out.println("Received: " + response);
			/*try {
				Thread.sleep(1000);
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}		
			GetMapState();//try again, we didn't screw our map last time*/
		}
	} //End void GetMapState()

	/**
	 * returns character corresponding to that cell of the map
	 * @param row
	 * @param col
	 * @return either T (tree), B (Building), + (coast), ^ (sea), # (rock)
	 *         or ~ (behind fog of war)
	 */
	public char GetMapCell(int row, int col) {
		return map[row][col];
	}
	
	public char[] getMapCellRow(int rowNumber) {
		return map[rowNumber];
	}
	
	public int[] getMapTileRow(int rowNumber) {
		return maptiles[rowNumber];
	}
	
	public void printMapCells() {
		this.printMapCells("MapCells.txt");
	}
	
	public void printMapCells(String fileName) {
		PrintStream outFile;
			try {
				outFile = new PrintStream (new FileOutputStream(fileName));
				for (int rowCount = 0 ; rowCount < this.GetMapLength(); rowCount++) {
					outFile.println(this.getMapCellRow(rowCount));
				}
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		
	}
	
	public void printMapTiles() {
		this.printMapTiles("MapTiles.txt");
	}
	
	public void printMapTiles(String fileName) {
		PrintStream outFile;
		try {
			outFile = new PrintStream (new FileOutputStream(fileName));
			for (int rowCount = 0 ; rowCount < this.GetMapLength(); rowCount++) {
				for (int colCount = 0; colCount < this.GetMapWidth() ; colCount++) {
					String mapTile = this.GetMapTile(rowCount, colCount);
					switch (mapTile.length()) {
					case 1:
						mapTile = mapTile + "   ";
						break;
					case 2:
						mapTile = mapTile + "  ";
						break;
					case 3:
						mapTile = mapTile + " ";
						break;
					default:
						System.out.println("ProxyBot.printMapTiles: unknown mapTile.length() - " + mapTile.length());
					}
					outFile.print(mapTile);
				}
				outFile.print("\n");
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	
	
	private int GetMapTileInt(int rowCount, int colCount) {
		return this.maptiles[rowCount][colCount];
	}

	public boolean canBuild(int x, int y, UnitType  unit) 
	{	
		int height = unit.getUnitHeight();
		//System.out.print("ProxyBot Unit Height:" + height);
		
		for(int i= 0; i<height; i++)
		{
			for(int j=0; j<height; j++)
			{
				//System.out.println("/t ProxyBot: " + (x+i) + "," + (y+j) + "," + CheckMapCell(x+i, y+j));
				if(i==0 && j==0)
					continue;
				
				if(CheckMapCell(x+i, y+j))
					return false;
			}
		}
		
		return true;
	}
	
	public boolean CheckMapCell(int col, int row) 
	{	
		if(row < 0 || row >= GetMapLength() || col < 0 || col >= GetMapWidth())
			return true;
		
		char cell = map[row][col];
		
		if(cell == 't' || cell == 'T' || cell == '^' || cell == 'B' || cell == 'b'|| cell == 'e' || cell == 'X' || cell == 'W' || cell == 'w' || cell == 'G' || cell == 'O' || cell == 'U')
			return true;
		else
			return false;
			
	}
	
	public boolean CheckMapCellTree(int col, int row) 
	{	
		if(row < 0 || row >= GetMapLength() || col < 0 || col >= GetMapWidth())
			return false;
		
		char cell = map[row][col];
 
		 
		if(cell == 't' || cell == 'T')
			return true;
		else
			return false;
			
	}
	
	public boolean CheckMapCellWall(int col, int row) 
	{	
		if(row < 0 || row >= GetMapLength() || col < 0 || col >= GetMapWidth())
			return false;
		
		char cell = map[row][col];
 
		 
		if(cell == 'w' || cell == 'W')
			return true;
		else
			return false;
			
	}
	
	
	
	public int GetMapLength() {
		return mapLength;
	}

	public int GetMapWidth() {
		return mapWidth;
	}

	/**
	 * orders wargus to advance a certain number of cycles and return
	 * @param N
	 */
	public void AdvanceNCycles(int N) {
		sendAndReceiveMessage("TRANSITION " + N + "\n", true);
	}

	/**
	 * uses the same scenario as was loaded
	 */
	public void RestartScenario() {
		sendAndReceiveMessage("RESTART\n", true);
	}

	/**
	 * self-documenting
	 * @param newSeed
	 */
	public void SendRandomNumSeedToStratagus(long newSeed) {
		sendAndReceiveMessage("Z " + newSeed + "\n", true);
	}

	/**
	 * uses to switch scenarios
	 * @param newMapPath
	 */
	public void LoadMapAndRestartScenario(String newMapPath) {
		sendAndReceiveMessage("MAP " + newMapPath + "\n", true);
		reset();
	}

	/**
	 * orders a unit to move to an x, y location. ignore enemies along the way
	 * @param unitID
	 * @param x
	 * @param y
	 * @param relative if false, use absolute positioning
	 */
	public void move(int unitID, int x, int y,
			boolean relative) {
		
		WargusUnit unit = findUnitByID(unitID, myUnits);
		
		if(unit != null) { //unit.getStatus()[0] == 0 || unit.getStatus()[0] == 2)

			if (relative) {
				//WargusUnit unit = findUnitByID(unitID, myUnits);
				x =  (x + unit.getLocX());
				y =  (y + unit.getLocY());
			}
			sendAndReceiveMessage("COMMAND " + unitID + " 2 " + x + " " + y + "\n", true);
		} else {
			System.out.println("ProxyBot - move: unitID("+unitID+") is NULL!!!!");
		}
	}
	
	/**
	 * orders a unit to move randomly. ignore enemies along the way
	 * @param unitID
	 * @param x
	 * @param y
	 * @param relative if false, use absolute positioning
	 */
	public void move_random(int unitID)
	{
		int x, y, map_x, map_y;
		double heading;
		
		boolean checkMap = false;
		
		Random m_gen = new Random();
		
		WargusUnit unit = findUnitByID(unitID, myUnits);
		
		if (unit == null) {
			System.out.println("ProxyBot - move_random: unitID("+unitID+") is NULL!!!!");
			return;
		}
		
		if(unit.getHeading() != -100.0)
		{
			
			 heading = unit.getHeading();
			 
			 x = (int) (1*Math.round(Math.cos(heading)));
			 y = (int) (1*Math.round(Math.sin(heading)));
			 
			 map_x = unit.getLocX() + x;
			 map_y = unit.getLocY() + y;
			
			 checkMap = CheckMapCell(map_x, map_y);
			 
			if(checkMap)//unit.getStatus()[0] == 0 && checkMap)
			{	
				
				double new_heading = unit.getHeading();
				
				int counter = 0;
				int MAX_PASS = 10;
				
				while(checkMap && counter < MAX_PASS)
				{
					int r_heading = m_gen.nextInt(5) - 2;
					
					new_heading = r_heading * (Math.PI / 4.0);
						
						
					new_heading = new_heading + unit.getHeading();
					
					 x = (int) (1*Math.round(Math.cos(new_heading)));
					 y = (int) (1*Math.round(Math.sin(new_heading)));
					 
					 map_x = unit.getLocX() + x;
					 map_y = unit.getLocY() + y;
					 
					 checkMap = CheckMapCell(map_x, map_y);
					 
					 counter++;
					 
					 if(counter == MAX_PASS)
					 {
						 new_heading = Math.PI;
					 }
				}
				
				 unit.setHeading(new_heading);
				 //move_random(unitID);
				 
			}
			
			int move_x = 4*x;
			int move_y = 4*y;
			
			if(move_x +  unit.getLocX() < 0)
				move_x = 0 - unit.getLocX();
			else if(move_x + unit.getLocX() > GetMapWidth())
				move_x = GetMapWidth() - 1 - unit.getLocX();
			
			if(move_y + unit.getLocY()< 0)
				move_y = 0 - unit.getLocY();
			else if(move_y + unit.getLocY() > GetMapLength())
				move_y = GetMapLength() - 1 - unit.getLocY();
			
			this.move(unitID,  move_x,  move_y, true);
				
		}
		
		else
		{
			
			heading = unit.getHeading();
			
			while(heading == unit.getHeading())
			{
				heading = (Math.PI / 4.0) * m_gen.nextInt(8);
			}
			
			unit.setHeading(heading);
			
			x = (int) Math.round(Math.cos(heading));
			y = (int) Math.round(Math.sin(heading));
			
			/*
			int move_x = 4*x;
			int move_y = 4*y;
			
			if(move_x < 0)
				move_x = 0;
			if(move_x > GetMapWidth())
				move_x = GetMapWidth() - 1;
			
			if(move_y < 0)
				move_y = 0;
			if(move_y > GetMapLength())
				move_y = GetMapLength() - 1;
			*/
			
			this.move(unitID,  x,  y, true);
		}
	
	}

	/**
	 * orders unit to attack given other unit
	 * @param unitIDOfAttacker
	 * @param attackThisUnitID
	 */
	public void attack(int unitIDOfAttacker, int attackThisUnitID) {
		System.out.println("Proxybot: ATTACK " + unitIDOfAttacker + " " + attackThisUnitID);
		String response = 
		sendAndReceiveMessage("COMMAND " + unitIDOfAttacker + " 4 " + attackThisUnitID + "\n", true);
		System.out.println("Proxybot: response is: " + response);
	}

	/**
	 * orders unit to cease all current actions and wait at current location.
	 * Note that unit may still move and attack if it sees another unit or is
	 * attacked from outside sight distance.
	 * @param unitID
	 */
	public void stop(int unitID) {
		sendAndReceiveMessage("COMMAND " + unitID + " 1\n", true);
	}

	/**
	 * Orders unit to attack a certain patch of ground. Only valid for ranged
	 * units.
	 * @param unit
	 * @param x
	 * @param y
	 * @param relative if false, use absolute positioning
	 */
	public void attackGround(int unitID, int x, int y, boolean relative) {
		if (relative) {
			WargusUnit unit = findUnitByID(unitID, myUnits);
			x =  (x + unit.getLocX());
			y =  (y + unit.getLocY());
		}
		System.out.println("Proxybot: ATTACKGROUND " + unitID + " " + x + " " + y);		
		String response = 
		sendAndReceiveMessage("COMMAND " + unitID + " 8 " + x + " " + y + "\n", true);
		System.out.println("Proxybot: response is: " + response);
	}

	/**
	 * move to location, attacking any enemies encountered on the way.
	 * @param unit
	 * @param x
	 * @param y
	 * @param relative if false, use absolute positioning
	 */
	public void attackMove(int unitID, int x, int y, boolean relative) {
		if (relative) {
			WargusUnit unit = findUnitByID(unitID, myUnits);
			x =  (x + unit.getLocX());
			y =  (y + unit.getLocY());
		}
		sendAndReceiveMessage("COMMAND " + unitID + " 17 " + x + " " + y + "\n", true);
	}

	/**
	 * unit and transport meet at nearest possible location and the unit
	 * enters the transport. Only valid for ground units
	 * @param unit
	 * @param transport
	 */
	public void boardTransport(int unitID, int transport) {
		sendAndReceiveMessage("COMMAND " + unitID + " 12 " + transport + " \n", true);
	}

	public void forwardstate(String filename)
	{
		String response = sendAndReceiveMessage("FORWARDSTATE " + filename +" \n", true);
		System.out.println("Proxybot: response is: " + response);

	}
	/** 
	 * constructs the given building at the given location, as long as there
	 * are no obstructions and there are enough resources. Note that a unit
	 * may move into the building footprint before construction and become
	 * and obstruction
	 * @param unit
	 * @param x
	 * @param y
	 * @param relative
	 * @param building
	 */
	public void build(int unitID, int x, int y, boolean relative,
			int building) {
		
		
		WargusUnit unit = findUnitByID(unitID, myUnits);
		
		if(unit.getStatus()[0] == 0)
		{
		if (relative) {
			//WargusUnit unit = findUnitByID(unitID, myUnits);
			x =  (x + unit.getLocX());
			y =  (y + unit.getLocY());
		}
		//System.out.println("Proxybot: BUILD " + unitID + " " + x + " " + y + " " + building);
		String response = 
		sendAndReceiveMessage("COMMAND " + unitID + " 3 " + building + " " + x + " " + y
				+ " \n", true);	
		}
		//System.out.println("Proxybot: response is: " + response);
	}

	/**
	 * casts a spell. ABL should only send appropriate spells for appropriate
	 * units
	 * @param unit
	 * @param x
	 * @param y
	 * @param relative
	 * @param spell
	 * @param target only needed if specified
	 */
	public void castSpell(int unitID, int x, int y, boolean relative,
			int spell, int target) {
		if (relative) {
			WargusUnit unit = findUnitByID(unitID, myUnits);
			x =  (x + unit.getLocX());
			y =  (y + unit.getLocY());
		}
		sendAndReceiveMessage("COMMAND " + unitID + " 16 " + x + " " + y + " " + spell 
				+ " " + target + " \n", true);	
	}

	/**
	 * this is the same as demolishing
	 * TODO need to change SpellID from 0 to what demolish really is.
	 * @param unit
	 * @param x
	 * @param y
	 * @param relative
	 */
	public void detonate(int unitID, int x, int y, boolean relative) {
		if (relative) {
			WargusUnit unit = findUnitByID(unitID, myUnits);
			x =  (x + unit.getLocX());
			y =  (y + unit.getLocY());
		}
		sendAndReceiveMessage("COMMAND " + unitID + " 16 " + x + " " + y 
				+ "0 -1 \n", true);
	}

	/**
	 * unit remains as close to leader as possible
	 * @param unit
	 * @param leader
	 */
	public void follow(int unitID, int leader) {
		sendAndReceiveMessage("COMMAND " + unitID + " 11 " + leader + " \n", true);
	}

	/**
	 * harvest whatever resource is at the specified location.
	 * @param unit
	 * @param x
	 * @param y
	 * @param relative if true, relative to the units location, else absolute
	 */
	public void harvest(int unitID, int x, int y, boolean relative) {
		if (relative) {
			WargusUnit unit = findUnitByID(unitID, myUnits);
			x =  (x + unit.getLocX());
			y =  (y + unit.getLocY());
		}
		//System.out.println("Proxybot: RESOURCE " + unitID + " " + x + " " + y);
		String response = 
		sendAndReceiveMessage("COMMAND " + unitID + " 6 0 " + x + " " + y 
				+ " \n", true);
		//System.out.println("Proxybot: response is: " + response);
	}
	
	/**
	 * pick a resource, harvest at closest available location
	 * @param unit
	 * @param type 1 == gold/oil, 2 == wood
	 */
	public void harvest(int unitID, int type) {
		if (type < 1 || type > 2) {
			System.out.println("Invalid Type of resource to harvest\nmust be 1 or 2\nvaluepassed: " + type);
			System.exit(1);
		}
		//System.out.println("Proxybot: RESOURCE " + unitID + " " + type);
		String response = 
		sendAndReceiveMessage("COMMAND " + unitID + " 6 " + type + " \n", true);
		//System.out.println("Proxybot: response is: " + response);
	}
	
	/**
	 * carry collected resources to nearest depot
	 * Probably not used much, since units return automatically after harvest
	 * @param unit
	 */
	public void returnWithResource(int unitID) {
		sendAndReceiveMessage("COMMAND " + unitID + " 7\n", true);
	}

	/**
	 * continously move from current location to given location and back as if
	 * with the attackMove command
	 * @param unit
	 * @param x
	 * @param y
	 * @param relative
	 */
	public void patrol(int unitID, int x, int y,
			boolean relative) {
		if (relative) {
			WargusUnit unit = findUnitByID(unitID, myUnits);
			x =  (x + unit.getLocX());
			y =  (y + unit.getLocY());
		}
		sendAndReceiveMessage("COMMAND " + unitID + " 9 " + x + " " + y + " \n", true);
		
	}

	/**
	 * repaits current unit given time and resources
	 * @param unit must be a peon or peasant
	 * @param building must be building, ballista/catapult, or naval unit
	 */
	public void repair(int unitID, int building) {
		sendAndReceiveMessage("COMMAND " + unitID + " 5 " + building + " \n", true);
	}

	/**
	 * begins researching given advance
	 * @param building
	 * @param advance
	 */
	public void research(int building, int advance) {
		sendAndReceiveMessage("COMMAND " + building + " 15 " + advance + " \n", true);		
	}

	/**
	 * orders unit to not move. Will still attack from given position
	 * @param unit
	 */
	public void standGround(int unitID) {
		sendAndReceiveMessage("COMMAND " + unitID + " 10\n", true);	
	}

	/**
	 * Train new units at barrack/town hall/etc
	 * @param building
	 * @param unitType
	 */
	public void train(int building, int unitType) {
		//System.out.println("Proxybot: TRAIN " + building + " " + unitType);
		String response = 
		sendAndReceiveMessage("COMMAND " + building + " 3 " + unitType + " \n", true);	
		//System.out.println("Proxybot: response is: " + response);
	}

	/**
	 * unload unit from transport at given location. Does not unload all units
	 * from transport. Units will be unloaded in order of this command being
	 * given, not in the optimal speed ordering
	 * @param unitID
	 * @param transport
	 * @param x
	 * @param y
	 * @param relative
	 */
	public void unloadTransport(int unitID, int transport, int x, int y,
			boolean relative) {
		if (relative) {
			WargusUnit unit = findUnitByID(unitID, myUnits);
			x =  (x + unit.getLocX());
			y =  (y + unit.getLocY());
		}
		sendAndReceiveMessage("COMMAND " + unitID + " 13 " + transport + " " + x + " " 
				+ y + " \n", true);	
	}

	/**
	 * upgrade to advanced building. Only for town centers and towers
	 * @param building
	 * @param advance
	 */
	public void upgrade(int building, int advance) {
		if (88 == advance) {//adjusting for UnitTypes
			advance = 58;
		} else if (90 == advance) {
			advance = 59;
		} else if (89 == advance) {
			advance = 103;
		} else if (91 == advance) {
			advance = 104;
		} else if (97 == advance) {
			advance = 106;
		} else if (99 == advance) {
			advance = 107;
		} else if (96 == advance) {
			advance = 61;
		} else if (98 == advance) {
			advance = 62;
		}
		String message = "COMMAND " + building + " 14 " + advance + " \n";
		sendAndReceiveMessage(message, true);	
	}

	/**
	 * checks that the returned value has all data
	 * @param stringToCheck
	 * @return if the returned string is complete
	 */
	public boolean _ParenMatch(String stringToCheck) {
		int openBraceCount = 0;
		int closeBraceCount = 0;

		// Go through the entire String and count open and close braces
		for (int i = 0; i < stringToCheck.length(); i++) {
			char c = stringToCheck.charAt(i);
			if (c == '(')
				openBraceCount++;
			else if (c == ')')
				closeBraceCount++;
		}

		return openBraceCount == closeBraceCount; // See if the counts agree
	}

	public WargusUnit findUnitByID(int id, ArrayList list){
		for(int i = 0; i < list.size(); i++) {
			if((((WargusUnit)(list.get(i))).getUnitID()) == id) {
				return (WargusUnit) list.get(i);
			}		
		}
		return null;
	}

	
	public int getNumOfPlayers() {
		return numOfPlayers;
	}

	
	/**
	 * returns the upgrades that have been researched
	 * need the creator due to a issue with the array being 
	 * found null on occaision
	 * @return the advances that have been researched (or not)
	 */
	public boolean[] getAdvances() {
		return advances;
	}
	

	/**
	 * stops the game remotely
	 */
	public void KillStratagus() {
		sendAndReceiveMessage("KILL\n", true);
	}

	/**
	 * stops playing but leaves the game running
	 */
	public void Quit() {
		sendAndReceiveMessage("QUIT\n", true);
	}

	/**
	 * used to atomically send and recieve messages, used to prevent 
	 * deadlock
	 * @param msg
	 * @param endlineCheck
	 * @return the response from the server
	 */
	public synchronized String sendAndReceiveMessage(String msg, 
			boolean endlineCheck) {
		String resp = null;
		
		if (!m_connected) {
			connect();
			if (!m_connected) return null;
		}
		
		try {
			lockSend();
			sendMsg(msg);
			resp = recvMessage(endlineCheck);		
			unlockSend();
			
			if (resp.equals("Ignored\n")) {
				//System.out.println("\n -------------------------------\n\n IGNORED COMMAND!!!!! \n\n -------------------------------\n");
				//System.out.println(System.currentTimeMillis());
				//ignoredMessages.add(msg);
			}
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(1);
		} 
		
		return resp;
	}
	
	/**
	 * sends the given msg across the wire to Wargus
	 * @param msg the command to be sent
	 */
	private void sendMsg(String msg) {
		try {
			
			last_message_sent = msg;
			mySocketOut.write(msg.getBytes());
			out.println(msg);
			mySocketOut.flush();
			
		} catch (Exception e) {
			//System.out.println("test");
			e.printStackTrace();
			System.exit(1);
		}
	}

	/** replacement for mySocket.receive() the original receive()
	 * returned bool -- we will just return the string itself on
	 * success and return 'null' on failure
	 * @param endlineCheck - checks for /n??
	 * @return the string received
	 */
	private String recvMessage(boolean endlineCheck) {
		String response = "", partialResponse;
		int numBytesRead;
		byte[] recvBuf = new byte[1024];
		long retries = 0, max_retries = 5;
		long start_time = System.currentTimeMillis();
		long maximum_wait = 5000;	// 5 seconds
		try {
			// Get the first part of the response (this may be the entire response)
			while(mySocketIn.available()==0) {
				if ((System.currentTimeMillis()-start_time) > maximum_wait) {
					System.out.println("recvMessage(1): More than " + (maximum_wait/1000) + " seconds waiting for the rest of the message from Stratagus.");
					System.out.println("recvMessage(1): The recieved message till now was: '" + response + "'");
					System.out.println("recvMessage(1): The last message sent to Stratagus was: '" + last_message_sent + "'");
					retries++;
					
					if (retries<=max_retries) {
/*
						mySocket.close();
						mySocket = new Socket();
						mySocket.setTcpNoDelay(true);
						mySocket.setSoLinger(true, 100);
						mySocket.connect(new InetSocketAddress(m_serverAddress, 4870));		
						mySocketIn = new BufferedInputStream(mySocket.getInputStream());
						mySocketOut = new BufferedOutputStream(mySocket.getOutputStream());						
*/
						System.out.println("recvMessage(1): Retrying...");			
						sendMsg(last_message_sent);
						start_time = System.currentTimeMillis();
					} else {
						throw new Exception("recvMessage(1): waited too long!");
					} // if
				}
				Thread.sleep(10);
			}			
			numBytesRead = mySocketIn.read(recvBuf);

			partialResponse = (numBytesRead > 0) ? new String(recvBuf, 0,
					numBytesRead) : "";
			
			while (true) {// Make sure we have the complete response
				response = response + partialResponse;
					
				// what type of "incomplete response"-check should I do?
				boolean responseIsComplete = endlineCheck ? response.endsWith("\n")
						: _ParenMatch(response);

				// If the response is size 0, or we deem the response is 
				// incomplete, keep trying to get the entire packet
				
				if ((response.length() == 0) || (responseIsComplete == false)) {
					while(mySocketIn.available()==0) {
						if ((System.currentTimeMillis()-start_time) > maximum_wait) {
							System.out.println("recvMessage(2): More than " + (maximum_wait/1000) + " seconds waiting for the rest of the message from Stratagus.");
							System.out.println("recvMessage(2): The recieved message till now was: '" + response + "'");
							throw new Exception("recvMessage(2): waited too long!");
						}
						Thread.sleep(10);
					}
					numBytesRead = mySocketIn.read(recvBuf);
					partialResponse = (numBytesRead > 0) ? new String(recvBuf,
							0, numBytesRead) : "";
				} else
					break;
			}
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(1);
		}
		return response;
	}

	/**
	 * just a getter
	 * @return value
	 */
	public ArrayList<WargusUnit> getEnemyUnits() {
		return enemyUnits;
	}

	/**
	 * just a getter
	 * @return value
	 */
	public ArrayList<WargusUnit> getFriendOrNeutralUnits() {
		return friendOrNeutralUnits;
	}

	/**
	 * just a getter
	 * @return value
	 */
	public synchronized ArrayList<WargusUnit> getMyUnits() {
		return myUnits;
	}

	/**
	 * just a getter
	 * @return value
	 */
	public static ProxyBot getProxyBot() {
		if (null == proxy) {
			System.out.println("!!!!!!!!!!WTF!!!!!!!!!!!!!!");
		}
		return proxy;
	}

	public long getCurrCycle(){
		return currCycle;
	}
	
	
	public void pause() {
		m_paused = true;
	}
	
	public void resume() {
		m_paused = false;
	}
	
	public boolean paused() {
		return m_paused;
	}
	
	public boolean connected() {
		return m_connected && m_reseted;
	}
	
	public WargusUnit getBuildingAt( int xPos, int yPos ) {
		
		WargusUnit unitFound = null;
		
		unitFound = this.checkListForBuilding(myUnits, xPos, yPos);
		if (unitFound == null)
			unitFound = this.checkListForBuilding(friendOrNeutralUnits, xPos, yPos);
		if (unitFound == null)
			unitFound = this.checkListForBuilding(enemyUnits, xPos, yPos);
		
		return unitFound;
		
	}
	
	
	
	private WargusUnit checkListForBuilding(ArrayList<WargusUnit> listOfUnits, int xPos, int yPos) {
		if( xPos >= 0 && xPos < this.mapWidth - 1 && yPos >= 0 && yPos < this.mapWidth - 1 ) {
			synchronized( listOfUnits ) {
				ArrayList<WargusUnit> deepCopy = new ArrayList<WargusUnit>(listOfUnits);
				for( WargusUnit currentUnit : deepCopy ) {
					
					if(currentUnit != null)	{
						int buildingWidth = WargusUnit.UnitWidth( currentUnit.getType() );
						int buildingHeight = WargusUnit.UnitHeight( currentUnit.getType() );
						int currXpos = currentUnit.getLocX();
						int currYpos = currentUnit.getLocY();
						//System.out.println( "Unit is type " + UnitType.getUnitTypeEnumFromNum(currentUnit.getType()).name());
						//System.out.println( "Unit is at " + currXpos + ", " + currYpos );
						//System.out.println( "xPos(" + xPos + ") yPos(" + yPos + ")");
						//System.out.println( "Unit is size " + buildingWidth + ", " + buildingHeight );
						
						if( xPos >= currXpos && xPos < currXpos + buildingWidth &&
								yPos >= currYpos && yPos < currYpos + buildingHeight ) {
	
							return currentUnit;
						}
					}
				}
			}
		}
		return null;
			
	}

	public UnitType getUnitTypeAt( int xPos, int yPos ) {
		UnitType returnType = UnitType.CORPSE;
		
		WargusUnit unit = getBuildingAt( xPos, yPos );
		if( unit != null )
			returnType = unit.getTypeEnum();
		
		return returnType;
	}
	
	public boolean checkAdjacentTerrainType( int xPos, int yPos, char terrainType ) {
		boolean returnVal = false;
		
		for( int i = -2; i <= 2; i++ )
			for( int j = -2; j <= 2; j++ ) {
				int checkX = xPos + i;
				int checkY = yPos + j;
				
				
				if( checkX >= 0 && checkY < this.mapWidth - 1 &&
						checkY >= 0 && checkY < this.mapLength - 1 &&
						map[checkY][checkX] == terrainType )
					returnVal = true;
			}
		
		return returnVal;
	}
	
	public boolean checkAdjacentUnit( int xPos, int yPos, UnitType type ) {
		boolean returnVal = false;
		
		for( int i = -2; i <= 2; i++ ) {
			for( int j = -2; j <= 2; j++ ) {
				//System.out.println("ProxyBot: checkAdjacentUnit(" + (xPos + i) + ", " + (yPos + j) + ") -- " + getUnitTypeAt( xPos + i, yPos + j ));
				if( getUnitTypeAt( xPos + i, yPos + j ) == type) {
					//System.out.println("ProxyBot - checkAdjacentUnit: xPos(" + (xPos + i) + ") yPos(" + (yPos + j) + "): " + type.name());
					returnVal = true;
				}
			}
		}
		
		//System.out.println("ProxyBot: checkAdjacentUnit - " + type.name() + ", " + returnVal);
		return returnVal;
	}
	
	public boolean checkAdjacentUnit(WargusUnit unit, UnitType type ) {
		boolean returnVal = false;
		
		double heading = unit.getHeading();
		
		int x = (int) (1 * Math.round(Math.cos(heading)));
		int y = (int) (1 * Math.round(Math.sin(heading)));

		int map_x = unit.getLocX() + x;
		int map_y = unit.getLocY() + y;
		
		if(getUnitTypeAt(map_x, map_y) == type)
			return true;
		
		 heading += Math.PI / 2.0;
		
		
		 x = (int) (1 * Math.round(Math.cos(heading)));
		 y = (int) (1 * Math.round(Math.sin(heading)));

		 map_x = unit.getLocX() + x;
		 map_y = unit.getLocY() + y;
		
		if(getUnitTypeAt(map_x, map_y) == type)
			return true;
		
		
		 heading = unit.getHeading() + 3.0 * Math.PI / 2.0;
			
			
		 x = (int) (1 * Math.round(Math.cos(heading)));
		 y = (int) (1 * Math.round(Math.sin(heading)));

		 map_x = unit.getLocX() + x;
		 map_y = unit.getLocY() + y;
		
		if(getUnitTypeAt(map_x, map_y) == type)
			return true;
		
		
		return false;
	}
	
	
	public synchronized ArrayList<WargusUnit> getNeutralUnits() {
		return friendOrNeutralUnits;
	}
}